package com.gitee.free.coding.core.aspect;

import com.alibaba.fastjson2.JSON;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 调用接口时打印日志
 */
@Slf4j
@Order(value = 999)
@Aspect
@Component
public class ServiceLogAspect {

    /**
     * 切入到所有的 Service 方法
     * 注意：如果 Service 接口中没有定义任何抽象方法，那么就不会为该类的子类进行动态代理。
     */
    @Pointcut("execution(* com.gitee.free.coding.admin..service.*.*(..))")
    public void pointcut() {
    }

    @Before("pointcut()")
    public void beforeAdvice(JoinPoint joinPoint) {
        // 转为 MethodSignature
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        // 获取 方法所属的类名 + 方法名
        String signatureName = methodSignature.getDeclaringTypeName() + " ==> " + methodSignature.getName();

        if (joinPoint.getArgs().length > 0) {
            // 获取方法参数类型数组
            Class<?>[] parameterTypes = methodSignature.getParameterTypes();
            List<String> argTypes = Arrays.stream(parameterTypes).map(Class::getSimpleName).map(m -> m.concat(".class")).collect(Collectors.toList());
            String argTypeString = String.join(", ", argTypes);
            log.info("调用接口有参方法开始：{}({})", signatureName, argTypeString);
            for (int i = 0; i < joinPoint.getArgs().length; i++) {
                log.info("---- 接口方法参数[{}]：{}", i, JSON.toJSONString(joinPoint.getArgs()[i]));
            }
        } else {
            log.info("调用接口无参方法开始：{}()", signatureName);
        }

    }

    @AfterReturning(pointcut = "pointcut()", returning = "result")
    public void afterReturningAdvice(JoinPoint joinPoint, Object result) {
        // 转为 MethodSignature
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        // 获取 方法所属的类名 + 方法名
        String signatureName = methodSignature.getDeclaringTypeName() + " ==> " + methodSignature.getName();

        if (joinPoint.getArgs().length > 0) {
            // result == null 代表目标方法返回类型为 void
            if (result == null) {
                log.info("调用接口有参方法结束，方法类型为void，无返回值。{}", signatureName);
                return;
            }
            log.info("调用接口有参方法结束。{}", signatureName);
        } else {
            // result == null 代表目标方法返回类型为 void
            if (result == null) {
                log.info("调用接口无参方法结束，方法类型为void，无返回值。{}", signatureName);
                return;
            }
            log.info("调用接口无参方法结束。{}", signatureName);
        }
        log.info("---- 方法返回类型：{}", result.getClass());
        log.info("---- 方法返回值：{}", JSON.toJSONString(result));
    }

}
